
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <math.h>
#include <pthread.h>

#include "httpmu.h"

#include "ssdp.h"

/*
* uuid -- xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx
*/
void ssdp_get_uuid(char *uuid)
{
    /*uint8_t *mac = LwIP_GetMAC(&xnetif[0]); ;
    sprintf(uuid, "smarthom-%04d-0000-0000-%02x%02x%02x%02x%02x%02x",
            rand(), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);*/
}


#define SSDP_MAX_DEV_NUM 10
typedef struct {
    httpmu_s_t *httpmu;
    ssdp_dev_t dev;  // local device
    httpmu_thread_t thread_id;
    int is_stop;
    httpmu_mutex_t mutex;
    httpmu_cond_t cond;
}ssdp_t;

ssdp_t g_ssdp_server;

void *ssdp_server_thread(void *arg)
{
    ssdp_t *server = (ssdp_t *)arg;
    server->is_stop = 0;
    while (!server->is_stop) {
        // TODO: maintain alive device cache
        httpmu_mutex_lock(&server->mutex);
        httpmu_cond_wait(&server->cond, &server->mutex);
        httpmu_mutex_unlock(&server->mutex);
        if (!server->is_stop)
            ssdp_notify_alive(&server->dev);
    }
    ssdp_notify_byebye(&server->dev);
    return NULL;
}

static int ssdp_msearch_request(http_head_t *request, const char *content, void *priv_data)
{
    ssdp_t *server = (ssdp_t *)priv_data;
    ssdp_dev_t *dev = &server->dev;
    const char *st = httpmu_get_option(request, "ST");
    printf("ssdp_msearch_request...\n\tST=%s\n", st);
    if (st && strcasecmp(st, dev->srv_type) == 0) {
        // notify ourself
        httpmu_mutex_lock(&server->mutex);
        httpmu_cond_signal(&server->cond);
        httpmu_mutex_unlock(&server->mutex);
    }
    return 0;
}

int ssdp_start_service(ssdp_dev_t *ssdp_dev)
{
    int ret = 0;
    
    memset(&g_ssdp_server, 0, sizeof(ssdp_t));
    memcpy(&g_ssdp_server.dev, ssdp_dev, sizeof(ssdp_dev_t));
    httpmu_mutex_init(&g_ssdp_server.mutex);
    httpmu_cond_init(&g_ssdp_server.cond);
    g_ssdp_server.is_stop = 0;
    ret = httpmu_start_thread(&g_ssdp_server.thread_id, ssdp_server_thread, &g_ssdp_server);
    if (ret < 0) {
        return -1;
    }
    
    httpmu_s_t *httpmu;
    httpmu = httpmu_create_server(SSDP_MULTICAST_IP, SSDP_MULTICAST_PORT);
    if (httpmu == NULL) {
        g_ssdp_server.is_stop = 1;
        httpmu_stop_thread(g_ssdp_server.thread_id);
        httpmu_mutex_free(&g_ssdp_server.mutex);
        httpmu_cond_free(&g_ssdp_server.cond);
        return -1;
    }
    g_ssdp_server.httpmu = httpmu;
    if (ssdp_dev) {
        ret = httpmu_server_set_resource(httpmu, ssdp_msearch_request, "M-SEARCH", &g_ssdp_server);
    }
    
    ret = httpmu_start_server(httpmu);    
    
    httpmu_mutex_lock(&g_ssdp_server.mutex);
    httpmu_cond_signal(&g_ssdp_server.cond);
    httpmu_mutex_unlock(&g_ssdp_server.mutex);
    
    return ret;
}

void ssdp_stop_service()
{
    httpmu_stop_server(g_ssdp_server.httpmu);
    httpmu_free_server(g_ssdp_server.httpmu);
    
    g_ssdp_server.is_stop = 1;
    httpmu_mutex_lock(&g_ssdp_server.mutex);
    httpmu_cond_signal(&g_ssdp_server.cond);
    httpmu_mutex_unlock(&g_ssdp_server.mutex);
    
    httpmu_stop_thread(g_ssdp_server.thread_id);
    httpmu_mutex_free(&g_ssdp_server.mutex);
    httpmu_cond_free(&g_ssdp_server.cond);
}

typedef struct {
    ssdp_dev_t *dev;
    int flag;
}search_t;

static int ssdp_notify_request(http_head_t *request, const char *content, void *priv_data)
{
    search_t *search = (search_t *)priv_data;
    ssdp_dev_t *dev = search->dev;
    const char *st = httpmu_get_option(request, "NT");
    printf("ssdp_notify_request...\n\tNT=%s\n", st);
    if (st && strcasecmp(st, dev->srv_type) == 0) {
        printf("get search device response\n");
        const char *opt = httpmu_get_option(request, "USN");
        if (opt) {
            memcpy(dev->srv_name, opt, sizeof(dev->srv_name));
            dev->srv_name[sizeof(dev->srv_name)-1] = 0;
        }
        opt = httpmu_get_option(request, "LOCATION");
        if (opt) {
            memcpy(dev->location, opt, sizeof(dev->location));
            dev->location[sizeof(dev->location)-1] = 0;
        }
        opt = httpmu_get_option(request, "CACHE-CONTROL");
        if (opt) {
            while (*opt++ != '=');
            dev->expiration = atoi(opt);
        }
        search->flag = 1;
    }
    return 0;
}

int ssdp_msearch(const char *st, int tmo, ssdp_dev_t *dev)
{
    memcpy(dev->srv_type, st, strlen(st) + 1);
    search_t search;
    search.dev = dev;
    search.flag = 0;
    int ret = httpmu_server_set_resource(g_ssdp_server.httpmu, ssdp_notify_request, "NOTIFY", &search);
    
    http_head_t *head = httpmu_create_head("M-SEARCH", "*");
    char buff[50];
    sprintf(buff, SSDP_MULTICAST_IP":%d", SSDP_MULTICAST_PORT);
    ret = httpmu_set_option(head, "HOST", buff);
    ret = httpmu_set_option(head, "MAN", "\"ssdp:discover\"");
    ret = httpmu_set_option(head, "MX", "1");
    ret = httpmu_set_option(head, "ST", st);
    httpmu_c_t * client = httpmu_create_client(SSDP_MULTICAST_IP, 0);
    if (client == NULL) {
        httpmu_free_head(head);
        return -1;
    }
    ret = httpmu_client_send_request(client, head, NULL);
    httpmu_free_client(client);
    httpmu_free_head(head);
    
    while (tmo-- && search.flag == 0) {
        httpmu_delay_sec(1);
        printf("delay ...\n");
    }
    ret = httpmu_server_del_resource(g_ssdp_server.httpmu, ssdp_notify_request);
    
    return search.flag;
}

int ssdp_notify_alive(ssdp_dev_t*dev)
{
    int ret = 0;
    http_head_t *head = httpmu_create_head("NOTIFY", "*");
    char buff[50];
    sprintf(buff, SSDP_MULTICAST_IP":%d", SSDP_MULTICAST_PORT);
    ret = httpmu_set_option(head, "HOST", buff);
    sprintf(buff, "max-age=%d", dev->expiration);
    ret = httpmu_set_option(head, "CACHE-CONTROL", buff);
    ret = httpmu_set_option(head, "NTS", "ssdp:alive");
    ret = httpmu_set_option(head, "LOCATION", dev->location); // 
    ret = httpmu_set_option(head, "NT", dev->srv_type);
    ret = httpmu_set_option(head, "USN", dev->srv_name);
    
    httpmu_c_t * client = httpmu_create_client(SSDP_MULTICAST_IP, 0);
    if (client == NULL) {
        httpmu_free_head(head);
        return -1;
    }
    ret = httpmu_client_send_request(client, head, NULL);
    httpmu_free_client(client);
    httpmu_free_head(head);
    
    return ret;
}

int ssdp_notify_byebye(ssdp_dev_t*dev)
{
    int ret = 0;
    http_head_t *head = httpmu_create_head("NOTIFY", "*");
    char buff[50];
    sprintf(buff, SSDP_MULTICAST_IP":%d", SSDP_MULTICAST_PORT);
    ret = httpmu_set_option(head, "HOST", buff);
    ret = httpmu_set_option(head, "NTS", "ssdp:byebye");
    ret = httpmu_set_option(head, "NT", dev->srv_type);
    ret = httpmu_set_option(head, "USN", dev->srv_name);
    
    httpmu_c_t * client = httpmu_create_client(SSDP_MULTICAST_IP, 0);
    if (client == NULL) {
        httpmu_free_head(head);
        return -1;
    }
    ret = httpmu_client_send_request(client, head, NULL);
    httpmu_free_client(client);
    httpmu_free_head(head);
    
    return ret;
}
